allwinner: Add functions to control CPU power/reset
authorSamuel Holland <[email protected]>
Sat, 12 Aug 2017 09:07:39 +0000 (04:07 -0500)
committerAndre Przywara <[email protected]>
Fri, 15 Jun 2018 10:45:24 +0000 (11:45 +0100)
sun50i_cpu_on will be used by the PSCI implementation to initialize
secondary cores for SMP. Unfortunately, sun50i_cpu_off is not usable by
PSCI directly, because it is not possible for a CPU to use this function
to power itself down. Power cannot be shut off until the outputs are
clamped, and MMIO does not work once the outputs are clamped.

But at least CPU0 can shutdown the other cores early in the BL31 boot
process and before shutting down the system.

Signed-off-by: Samuel Holland <[email protected]>
Signed-off-by: Andre Przywara <[email protected]>
plat/allwinner/common/sunxi_bl31_setup.c
plat/allwinner/common/sunxi_cpu_ops.c [new file with mode: 0644]
plat/allwinner/common/sunxi_pm.c
plat/allwinner/common/sunxi_private.h
plat/allwinner/sun50i_a64/include/sunxi_cpucfg.h [new file with mode: 0644]
plat/allwinner/sun50i_a64/platform.mk

index 6331238178d2adf58a62c7c3f3751559c170eee1..22abafe922bf956436ed51230d1b1e950f196b4f 100644 (file)
@@ -44,6 +44,9 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1,
        bl33_image_ep_info.spsr = SPSR_64(MODE_EL2, MODE_SP_ELX,
                                          DISABLE_ALL_EXCEPTIONS);
        SET_SECURITY_STATE(bl33_image_ep_info.h.attr, NON_SECURE);
+
+       /* Turn off all secondary CPUs */
+       sunxi_disable_secondary_cpus(plat_my_core_pos());
 }
 
 void bl31_plat_arch_setup(void)
diff --git a/plat/allwinner/common/sunxi_cpu_ops.c b/plat/allwinner/common/sunxi_cpu_ops.c
new file mode 100644 (file)
index 0000000..be72dee
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <debug.h>
+#include <mmio.h>
+#include <platform_def.h>
+#include <sunxi_mmap.h>
+#include <sunxi_cpucfg.h>
+#include <utils_def.h>
+
+#include "sunxi_private.h"
+
+static void sunxi_cpu_disable_power(unsigned int cluster, unsigned int core)
+{
+       if (mmio_read_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core)) == 0xff)
+               return;
+
+       INFO("PSCI: Disabling power to cluster %d core %d\n", cluster, core);
+
+       mmio_write_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core), 0xff);
+}
+
+static void sunxi_cpu_enable_power(unsigned int cluster, unsigned int core)
+{
+       if (mmio_read_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core)) == 0)
+               return;
+
+       INFO("PSCI: Enabling power to cluster %d core %d\n", cluster, core);
+
+       /* Power enable sequence from original Allwinner sources */
+       mmio_write_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core), 0xfe);
+       mmio_write_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core), 0xf8);
+       mmio_write_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core), 0xe0);
+       mmio_write_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core), 0x80);
+       mmio_write_32(SUNXI_CPU_POWER_CLAMP_REG(cluster, core), 0x00);
+}
+
+void sunxi_cpu_off(unsigned int cluster, unsigned int core)
+{
+       INFO("PSCI: Powering off cluster %d core %d\n", cluster, core);
+
+       /* Deassert DBGPWRDUP */
+       mmio_clrbits_32(SUNXI_CPUCFG_DBG_REG0, BIT(core));
+       /* Activate the core output clamps */
+       mmio_setbits_32(SUNXI_POWEROFF_GATING_REG(cluster), BIT(core));
+       /* Assert CPU power-on reset */
+       mmio_clrbits_32(SUNXI_POWERON_RST_REG(cluster), BIT(core));
+       /* Remove power from the CPU */
+       sunxi_cpu_disable_power(cluster, core);
+}
+
+void sunxi_cpu_on(unsigned int cluster, unsigned int core)
+{
+       INFO("PSCI: Powering on cluster %d core %d\n", cluster, core);
+
+       /* Assert CPU core reset */
+       mmio_clrbits_32(SUNXI_CPUCFG_RST_CTRL_REG(cluster), BIT(core));
+       /* Assert CPU power-on reset */
+       mmio_clrbits_32(SUNXI_POWERON_RST_REG(cluster), BIT(core));
+       /* Set CPU to start in AArch64 mode */
+       mmio_setbits_32(SUNXI_CPUCFG_CLS_CTRL_REG0(cluster), BIT(24 + core));
+       /* Apply power to the CPU */
+       sunxi_cpu_enable_power(cluster, core);
+       /* Release the core output clamps */
+       mmio_clrbits_32(SUNXI_POWEROFF_GATING_REG(cluster), BIT(core));
+       /* Deassert CPU power-on reset */
+       mmio_setbits_32(SUNXI_POWERON_RST_REG(cluster), BIT(core));
+       /* Deassert CPU core reset */
+       mmio_setbits_32(SUNXI_CPUCFG_RST_CTRL_REG(cluster), BIT(core));
+       /* Assert DBGPWRDUP */
+       mmio_setbits_32(SUNXI_CPUCFG_DBG_REG0, BIT(core));
+}
+
+void sunxi_disable_secondary_cpus(unsigned int primary_cpu)
+{
+       for (unsigned int cpu = 0; cpu < PLATFORM_CORE_COUNT; cpu += 1) {
+               if (cpu == primary_cpu)
+                       continue;
+               sunxi_cpu_off(cpu / PLATFORM_MAX_CPUS_PER_CLUSTER,
+                              cpu % PLATFORM_MAX_CPUS_PER_CLUSTER);
+       }
+}
index c73400e120de98f1d3ac3946f334aa155fb5b26e..fb3842dbfaf25da5a7ff7a14fbe450db9b935656 100644 (file)
 #define SUNXI_WDOG0_CFG_REG            (SUNXI_WDOG_BASE + 0x0014)
 #define SUNXI_WDOG0_MODE_REG           (SUNXI_WDOG_BASE + 0x0018)
 
+#include "sunxi_private.h"
+
 static void __dead2 sunxi_system_off(void)
 {
+       /* Turn off all secondary CPUs */
+       sunxi_disable_secondary_cpus(plat_my_core_pos());
+
        ERROR("PSCI: Full shutdown not implemented, halting\n");
        wfi();
        panic();
index 34e5639d401609831f43f3bc2dff47f4c2b4e947..bd923f4090e9b04686d981d526831061d87f320c 100644 (file)
@@ -8,5 +8,8 @@
 #define __SUNXI_PRIVATE_H__
 
 void sunxi_configure_mmu_el3(int flags);
+void sunxi_cpu_off(unsigned int cluster, unsigned int core);
+void sunxi_cpu_on(unsigned int cluster, unsigned int core);
+void sunxi_disable_secondary_cpus(unsigned int primary_cpu);
 
 #endif /* __SUNXI_PRIVATE_H__ */
diff --git a/plat/allwinner/sun50i_a64/include/sunxi_cpucfg.h b/plat/allwinner/sun50i_a64/include/sunxi_cpucfg.h
new file mode 100644 (file)
index 0000000..049c2ad
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __SUNXI_CPUCFG_H__
+#define __SUNXI_CPUCFG_H__
+
+#include <sunxi_mmap.h>
+
+/* c = cluster, n = core */
+#define SUNXI_CPUCFG_CLS_CTRL_REG0(c)  (SUNXI_CPUCFG_BASE + 0x0000 + (c) * 16)
+#define SUNXI_CPUCFG_CLS_CTRL_REG1(c)  (SUNXI_CPUCFG_BASE + 0x0004 + (c) * 16)
+#define SUNXI_CPUCFG_CACHE_CFG_REG0    (SUNXI_CPUCFG_BASE + 0x0008)
+#define SUNXI_CPUCFG_CACHE_CFG_REG1    (SUNXI_CPUCFG_BASE + 0x000c)
+#define SUNXI_CPUCFG_DBG_REG0          (SUNXI_CPUCFG_BASE + 0x0020)
+#define SUNXI_CPUCFG_GLB_CTRL_REG      (SUNXI_CPUCFG_BASE + 0x0028)
+#define SUNXI_CPUCFG_CPU_STS_REG(c)    (SUNXI_CPUCFG_BASE + 0x0030 + (c) * 4)
+#define SUNXI_CPUCFG_L2_STS_REG                (SUNXI_CPUCFG_BASE + 0x003c)
+#define SUNXI_CPUCFG_RST_CTRL_REG(c)   (SUNXI_CPUCFG_BASE + 0x0080 + (c) * 4)
+#define SUNXI_CPUCFG_RVBAR_LO_REG(n)   (SUNXI_CPUCFG_BASE + 0x00a0 + (n) * 8)
+#define SUNXI_CPUCFG_RVBAR_HI_REG(n)   (SUNXI_CPUCFG_BASE + 0x00a4 + (n) * 8)
+
+#define SUNXI_CPU_POWER_CLAMP_REG(c, n)        (SUNXI_R_PRCM_BASE + 0x0140 + \
+                                        (c) * 16 + (n) * 4)
+#define SUNXI_POWEROFF_GATING_REG(c)   (SUNXI_R_PRCM_BASE + 0x0100 + (c) * 4)
+#define SUNXI_R_CPUCFG_CPUS_RST_REG    (SUNXI_R_CPUCFG_BASE + 0x0000)
+#define SUNXI_POWERON_RST_REG(c)       (SUNXI_R_CPUCFG_BASE + 0x0030 + (c) * 4)
+#define SUNXI_R_CPUCFG_SYS_RST_REG     (SUNXI_R_CPUCFG_BASE + 0x0140)
+#define SUNXI_R_CPUCFG_SS_FLAG_REG     (SUNXI_R_CPUCFG_BASE + 0x01a0)
+#define SUNXI_R_CPUCFG_CPU_ENTRY_REG   (SUNXI_R_CPUCFG_BASE + 0x01a4)
+#define SUNXI_R_CPUCFG_SS_ENTRY_REG    (SUNXI_R_CPUCFG_BASE + 0x01a8)
+#define SUNXI_R_CPUCFG_HP_FLAG_REG     (SUNXI_R_CPUCFG_BASE + 0x01ac)
+
+#endif /* __SUNXI_CPUCFG_H__ */
index 074a64b43bff6bc1084223924e2bfb2b1553164e..49764e09a7afcc2fdb5d54d4f87ef06f4f93cbf9 100644 (file)
@@ -28,6 +28,7 @@ BL31_SOURCES          +=      drivers/arm/gic/common/gic_common.c     \
                                plat/common/plat_gicv2.c                \
                                plat/common/plat_psci_common.c          \
                                ${AW_PLAT}/common/sunxi_bl31_setup.c    \
+                               ${AW_PLAT}/common/sunxi_cpu_ops.c       \
                                ${AW_PLAT}/common/sunxi_pm.c            \
                                ${AW_PLAT}/common/sunxi_topology.c